package com.sxhuayuan.parking.compenent.web;

import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.sxhuayuan.parking.compenent.Message;
import com.sxhuayuan.parking.compenent.domain.Setting.AccountLockType;
import com.sxhuayuan.parking.exception.MyException;
import com.sxhuayuan.parking.exception.SessionTimeoutException;
import com.sxhuayuan.parking.exception.ViewException;
import com.sxhuayuan.parking.utils.SettingUtils;

@ControllerAdvice
public class GlobalDefaultExceptionHandler {
	private static Logger log = LoggerFactory.getLogger(GlobalDefaultExceptionHandler.class);

	@ExceptionHandler(ViewException.class)
	public ModelAndView viewExceptionHandler(ViewException e) {
		ModelAndView mv = new ModelAndView("/common/error");
		mv.addObject("error_msg", e.getMessage());
		return mv;
	}
	
	/**
	 * 未授权异常处理
	 */
	@ExceptionHandler(UnauthorizedException.class)
	public @ResponseBody RespMessage unauthorizedHandler(HttpServletRequest req, HttpServletResponse response, Exception e) {
		// 打印异常信息：
		response.setStatus(403);
		return RespMessage.noPermissionError();
	}

	@ExceptionHandler(BindException.class)
	public @ResponseBody RespMessage bindExceptionHandler(BindException ex) {
		// ex.getFieldError():随机返回一个对象属性的异常信息。如果要一次性返回所有对象属性异常信息，则调用ex.getAllErrors()
		FieldError fieldError = ex.getFieldError();
		StringBuilder sb = new StringBuilder();
		sb.append("Field error in object '").append(fieldError.getObjectName()).append("' on field '").append(fieldError.getField()).append("': rejected value: ")
				.append(ObjectUtils.nullSafeToString(fieldError.getRejectedValue())).append(".");
		// 生成返回结果
		RespMessage errorResult = RespMessage.validError();
		errorResult.setErr(sb.toString());
		return errorResult;
	}

	@ExceptionHandler(ConstraintViolationException.class)
	public @ResponseBody RespMessage constraintViolationExceptionHandler(ConstraintViolationException ex) {
		Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
		for (ConstraintViolation<?> c : constraintViolations) {
			log.debug(c.getMessage());
		}
		return RespMessage.error(Message.COMMON_SAVE_FAIL);
	}

	/**
	 * 入参验证失败
	 * 
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public @ResponseBody RespMessage methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
		BindingResult result = ex.getBindingResult();
		List<FieldError> list = result.getFieldErrors();
		StringBuilder err = new StringBuilder();
		list.stream().forEach((e) -> {
			err.append(String.format("[%s]%s", e.getField(), e.getDefaultMessage())).append(",");
		});
		return RespMessage.builder().validError().setErr(err.deleteCharAt(err.length() - 1).toString()).build();
	}

	@ExceptionHandler(IllegalArgumentException.class)
	public RespMessage illegalArgumentExceptionHandler(IllegalArgumentException ex) {
		return RespMessage.builder().validError().setErr(ex.getMessage()).build();
	}

	@ExceptionHandler(AuthenticationException.class)
	public @ResponseBody RespMessage shiroExceptionHandler(AuthenticationException e) {
		String message = "";
		switch (e.getClass().getName()) {
		case "org.apache.shiro.authc.pam.UnsupportedTokenException":
			message = Message.LOGIN_UNSUPPORTED_TOKEN;
			break;
		case "org.apache.shiro.authc.UnknownAccountException":
			message = Message.LOGIN_ACCOUNT_INCORRECT;
			break;
		case "org.apache.shiro.authc.DisabledAccountException":
			message = Message.LOGIN_DISABLED_ACCOUNT;
			break;
		case "org.apache.shiro.authc.LockedAccountException":
			message = Message.LOGIN_LOCKED_ACCOUNT;
			break;
		case "org.apache.shiro.authc.IncorrectCredentialsException":
			if (ArrayUtils.contains(SettingUtils.get().getAccountLockTypes(), AccountLockType.admin)) {
				message = Message.LOGIN_ACCOUNT_LOCK_COUNT;
			} else {
				message = Message.LOGIN_ACCOUNT_INCORRECT;
			}
			break;
		case "org.apache.shiro.authc.AuthenticationException":
			message = Message.LOGIN_ACCOUNT_AUTHENTICATION;
			break;
		default:
			message = Message.LOGIN_ACCOUNT_AUTHENTICATION;
			break;
		}
		return RespMessage.builder().error().setErr(message, SettingUtils.get().getAccountLockTime()).build();
	}

	@ExceptionHandler(org.apache.shiro.session.UnknownSessionException.class)
	public @ResponseBody RespMessage unknownSession() {
		return RespMessage.error(Message.COMMON_NON_PERMISSION);
	}

	@ExceptionHandler(MyException.class)
	public @ResponseBody RespMessage myExceptionHandler(MyException e) {
		return RespMessage.error(e.getStatus(), e.getMessage());
	}

	@ExceptionHandler(SessionTimeoutException.class)
	public @ResponseBody RespMessage sessionTimeoutExceptionHandler(SessionTimeoutException e) {
		SecurityUtils.getSecurityManager().logout(SecurityUtils.getSubject());
		return RespMessage.error(RespMessage.Status.INVALID_TOKEN, "登录超时，请重新登录");
	}

	/**
	 * 未知异常处理
	 */
	@ExceptionHandler(Exception.class)
	public @ResponseBody RespMessage defaultErrorHandler(HttpServletRequest req, HttpServletResponse response, Exception e) {
		// 打印异常信息：
		log.error("defaultErrorHandler:", e);
		if (e instanceof HttpRequestMethodNotSupportedException) {
			response.setStatus(HttpStatus.BAD_REQUEST.value());
			return RespMessage.error(Message.EXP_METHOD_NOT_SUPPORTED);
		} else if (e instanceof HttpMessageNotReadableException) {
			response.setStatus(HttpStatus.BAD_REQUEST.value());
			return RespMessage.error(Message.EXP_PARAMS_NOT_READABLE);
		} else if (e instanceof MyException) {
			/** 自定义异常 */
			return RespMessage.error(((MyException) e).getStatus(), ((MyException) e).getMessage());
		} else {
			return RespMessage.builder().unknownError().setErr(e.getClass() + "").build();
		}
	}
}